package individual.ircon.bilibili.network;

import com.alibaba.fastjson.JSON;
import individual.ircon.bilibili.bean.DanmuInfo;
import individual.ircon.bilibili.bean.RoomInit;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.Arrays;

import static individual.ircon.bilibili.bean.EnteryRoom.getEnteryRoom;

public class DanmuHttpApi {
    private DanmuHttpApi(){}

    /// === 工具 ===

    /**
     * 获取一个设置号的HttpClient。
     * @return HttpClinet实例。
     */
    private static HttpClient getClient() {
        // 设置网络客户端
        var build = HttpClient.newBuilder();
        build.connectTimeout(Duration.ofSeconds(3));
        build.version(HttpClient.Version.HTTP_2);
        build.followRedirects(HttpClient.Redirect.NORMAL);

        return build.build();
    }

    /**
     * 根据链接，实例化一个请求。
     * @param url 链接
     * @return 请求
     * @throws URISyntaxException 如果链接有问题。
     */
    private static HttpRequest getRequest(String url) throws URISyntaxException {
        URI uri = new URI(url);
        return HttpRequest.newBuilder().uri(uri).GET().build();
    }

    /**
     * 获取链接弹幕服务器需要的token.
     * @param info 服务器信息对象
     * @return token
     */
    private static String getDanmuToken(DanmuInfo info){
        return info.getData().getToken();
    }

    /**
     * 获取所有可用的弹幕服务器的链接。
     * @param info 弹幕服务信息的队形
     * @return 所有的弹幕服务器的链接。
     */
    private static String[] getDanmuServiceHost(DanmuInfo info) {
        var array = info.getData().getHostList();
        String[] ans = new String[array.size()];
        for(int i = 0 ; i < ans.length ; ++i) {
            var h = array.get(i);
            ans[i] = getWssUrl(h.getHost(), h.getWssPort());
        }
        return ans;
    }

    /**
     * 生成wss的uri
     * @param host 主机地址
     * @param port 端口
     * @return uri
     */
    private static String getWssUrl(String host, int port) {
        return String.format("wss://%s:%d/sub", host, port);
    }



    /// === === === === === ===

    /**
     * 用于获取弹幕服务器和token的API。
     */
    private static final String DANMU_SERVICE_INFO_API =
            "https://api.live.bilibili.com/xlive/web-room/v1/index/getDanmuInfo?id=%s&type=0";

    /**
     * 获取弹幕服务器的信息。其实主要是为了获取token。
     * @param room_id 房间的id
     * @return Bean类，JSON类型的
     * @throws HttpApiException 如果执行API失败。
     */
    public static DanmuInfo getDanmuInfo(long room_id) throws HttpApiException{
        // 设置请求
        String url = String.format(DANMU_SERVICE_INFO_API, Long.toString(room_id));
        HttpRequest request = null;
        try {
            request = getRequest(url);
        } catch (URISyntaxException e) {
            throw new RuntimeException("弹幕服务器链接错误！！！", e);
        }

        // 处理相应结果
        HttpClient client = getClient();
        HttpResponse<String> response = null;
        try {
            response = client.send(request, HttpResponse.BodyHandlers.ofString());
        } catch (IOException | InterruptedException e) {
            throw new HttpApiException("获取弹幕服务器信息失败", e);
        }
        return JSON.parseObject(response.body(), DanmuInfo.class);
    }

    /**
     * 用于获取房间真实ID的API链接。
     */
    private static final String REAL_ROOM_ID_API =
            "https://api.live.bilibili.com/room/v1/Room/room_init?id=%s";

    /**
     * 获取房间真实id，链接里面的id不一定是房间id。
     * @param room_id 链接里面的房间id
     * @return 相应的数据队形
     * @throws HttpApiException 如果访问API出现错误。
     */
    public static RoomInit roomInit(long room_id) throws HttpApiException {
        // 准备请求
        String url = String.format(REAL_ROOM_ID_API, Long.toString(room_id));
        HttpRequest request = null;
        try {
            request = getRequest(url);
        } catch (URISyntaxException e) {
            throw new RuntimeException("房间真实ID服务器链接错误！！！", e);
        }

        // 处理相应结果
        HttpClient client = getClient();
        HttpResponse<String> response = null;
        try {
            response = client.send(request, HttpResponse.BodyHandlers.ofString());
        } catch (IOException | InterruptedException e) {
            throw new HttpApiException("获取房间真实链接失败！！！",e);
        }
        return JSON.parseObject(response.body(), RoomInit.class);
    }

    /**
     * bean工具类。存储直播房间的真实地址，握手用的Json，弹幕服务器的地址。
     */
    public static final class HandshakeInfo {
        /**
         * 房间真实ID
         */
        public long RealID;
        /**
         * 握手用的JSON
         */
        public String HandshakeJson;
        /**
         * 弹幕服务器的地址
         */
        public String[] ServiceHost;

        @Override
        public String toString() {
            return "HandshakeInfo{" +
                    "RealID=" + RealID +
                    ", HandshakeJson='" + HandshakeJson + '\'' +
                    ", ServiceHost=" + Arrays.toString(ServiceHost) +
                    '}';
        }
    }

    /**
     * 获取链接用的json和弹幕服务器的地址。
     * @param room_id 房间的链接ID
     * @return 握手信息
     * @throws HttpApiException 如果API链接出现错误。
     */
    public static HandshakeInfo getHandshakeInfo(long room_id) throws HttpApiException{
        // 获取真实房间ID
        var init = roomInit(room_id);
        long real_id = init.data.roomId;

        // 获取服务器信息
        var danmu = getDanmuInfo(real_id);
        String token = getDanmuToken(danmu);
        String[] host = getDanmuServiceHost(danmu);

        // 获取进入房间的json
        var er = getEnteryRoom(real_id, token);
        String body = JSON.toJSONString(er);

        HandshakeInfo info = new HandshakeInfo();
        info.RealID = real_id;
        info.HandshakeJson = body;
        info.ServiceHost = host;

        return info;
    }
}
